/** @jsxImportSource @emotion/react */
import {jsx,css} from "@emotion/react";
import * as React from "react";
import {CSSProperties} from "react";
import { useSprings, animated } from '@react-spring/web';
import {useDrag, useGesture} from '@use-gesture/react';
import {Vector2} from "@use-gesture/core/dist/declarations/src/types/utils";
// import { useMeasure } from "./Hooks/use-measure";
// import { usePrevious } from "./Hooks/previous";
// import {useMMenuBarOcup} from "./MainMenuBar";
import {
    usePrevious,useMMenuBarOcup,
} from "customize-easy-ui-component";
import {useMeasure} from "customize-easy-ui-component/esm/Hooks/use-measure";


export interface GesturePageProps  {
    id?: string | undefined;
    //代表是当前显示哪一个儿子; onRequestChange配合执行，上级组件决定最终 value：index 该显示的儿子序号。
    value: number;
    //回调触发：切换到哪一个儿子去了
    //【注意】onRequestChange 必须把 value 参数值进行修改，否则无法真的切换。支持 异步的 懒惰加载组件数据。
    onRequestChange: (value: number) => void;

    //允许很长儿子队列只需要加载一部分儿子。
    lazyLoad?: boolean;      //懒加载总开关: 允许某些儿子mount实际时间滞后。
    //允许部分加载机制： 依据suggestedIndex来回调设置应当同时给与加载的那些兄弟，不是都是拖延加载的做法;
    onSetLazy?: (suggestedIndex: number) => number[];
    /* 拉不动了： 转交给上一级的轮播台机会拉动 ；
       beyondX 拉拽X方向已经超限制了：=-1 表示X轴左边超出  =1 表示X轴右边超出。  beyondY 高度方向的超出。
    * */
    onNestDragEnd?: (direction: Vector2) => boolean;

    //不能使用这样 paneStyle?: SerializedStyles;定义； 没法注入 style:{};
    paneStyle?: CSSProperties | undefined;

    children: Array<React.ReactNode>;
    //children: Array<React.ReactNode | CallbackProps>;
    //style: 隐藏 在HTMLAttributes里面
    style?: CSSProperties | undefined;
    //要不要允许手势操作处理，允许拉拽？
    noBind?: boolean;
    //电脑屏为了确保search input cancel触发最好改triggerAllEvents: false,
    // searchCancel?: boolean;
}

//向上级组件暴露的操作接口
export interface PagerHandles {
    focus(i?: number): void;
}

/*任意内容 轮播台 类似于 CarouselPage ；
文档例子：https://codesandbox.io/s/github/pmndrs/use-gesture/tree/main/demo/src/sandboxes/draggable-list
* */
export const Pager  = React.forwardRef(
    ( {
          children,
          id,
          value: index,
          onRequestChange,
          lazyLoad = false,
          onSetLazy,
          onNestDragEnd,
          style,
          paneStyle,
          noBind=false,
          ...other
      } : GesturePageProps,
      ref
    ) =>  {
        const indexRef = React.useRef(index)
        const containerRef = React.useRef(null);      //绑定 拉拽的第一层Div;
        const { bounds } = useMeasure(containerRef);
        const { width,  } = bounds;
        //已经加载的部分： 多个儿子，不一定都加载mount完毕; 轮播切换不会卸载。 [index]默认只能加载一个;
        const [loaded, setLoaded] = React.useState(
            () => new Set(onSetLazy ? onSetLazy(index) : [index])
        );
        //当前已经加载的儿子映射DOM;
        const childrenRefs = React.useRef<Map<number, HTMLDivElement | null>>(
            new Map()
        );
        const previousIndex = usePrevious(index);
        const shouldFocusRef = React.useRef<number | null>(index || null);

        React.useEffect(() => {
            if (typeof previousIndex === "number" && previousIndex !== index) {
                shouldFocusRef.current = index;
            } else {
                shouldFocusRef.current = null;
            }
        }, [previousIndex, index]);
        //直接定位滚到某个儿子吗？
        function focusByIndex(i: number) {
            const el = childrenRefs.current.get(i);
            if (el) {
                el.focus();
            }
        }
        // expose an imperative focus function which focuses the currently active index
        //上级组件可能使用本接口直接操作 focusByIndex切换到某一个儿子， 跳过轮播滑动:也是可能切换轮播台的。
        React.useImperativeHandle(ref, () => ({
            focus: (i?: number) => {
                focusByIndex(i || index);
            }
        }));

        const renderableChildren = children.filter(child => child !== null);
        const childCount = renderableChildren.length;

        function isValidNextIndex(index: number) {
            return index >= 0  && index <= (childCount - 1);
        }

        /** 跟踪设置应该加载的儿子;
         * We keep a set of indexes that should
         * be loaded for lazy loading.
         * @param index:, 应该加载的儿子
         */
        function addIndexToLoaded(index: number) {
            if(!isValidNextIndex(index))    return;
            let indexes: number | number[] = index;
            // allow the user to customize which indexes to load
            if (onSetLazy) {
                indexes = onSetLazy(index);
            }
            const indexesArray = Array.isArray(indexes) ? indexes : [indexes];
            const next = new Set(loaded);
            //重新洗牌 next= 旧的已经加载儿子们；
            indexesArray.forEach(i => {
                // don't set items which are already loaded or are invalid
                if (loaded.has(i) || !isValidNextIndex(index)) {
                    return;
                }
                next.add(i);
            });
            setLoaded(next);
        }

        const {touchScr, carousel} = useMMenuBarOcup(false);

        const [anmprops, api] = useSprings(childCount, i => ({
            x: i * width,
            scale: 1,
            display: 'block',
        }));

        /*配置困难：为了确保电脑模式下的search input的cancel X 按钮的点击可清除编辑框内容，修改pointer: { mouse: true },
        拉拽触发时机是鼠标左键释放或触摸屏按压离开后。
        拉拽Div可以跟随同步位置位移动画。触发后附带onRequestChange=setIndex+useEffect[index]动画一次性变更x动画值。
        多个儿子页面布局，并排的儿子页面 x = (i - indexRef.current) * width 依赖绝对定位x偏移水平方向坐标位移。这里x来自react-spring加持不是css原生。
        * */
        const bind = useDrag((state) => {
                const { active, movement: [mx,my], direction: [xDir,yDir] , cancel,
                    swipe:[swpx,], last,  canceled,tap, event } = state;
                console.log("Pr拉动",active,"state=",state);
                event.stopPropagation();   //加了没法让菜单点外区关闭；    #不依靠direction[]swipe[]这两组有问题啊？
                if(!active && last && !canceled && !tap && (Math.abs(swpx)>0 || (Math.abs(mx)>=width*0.01 && Math.abs(mx)>Math.abs(my) ) ) ) {
                    let finalDir= swpx!==0? swpx : mx>0? 1 : -1;
                    //允许水平方向滑动
                    if(finalDir!==0){
                        let Old=indexRef.current;
                        const idxNew= indexRef.current + (finalDir>0 ? -1 : 1);
                        const objIndex= (idxNew > childCount - 1)? (childCount - 1) : idxNew<0? 0 :idxNew;
                        // console.log("拉动End Old",Old,"objIndex=",objIndex);
                        if(objIndex===Old){
                            cancel();
                            onNestDragEnd && onNestDragEnd([finalDir,0]);
                            console.log("界线拉拽没用",indexRef.current,"objIndex=",objIndex);
                        }
                        else {
                            //滑动未到位就该触发懒加载的准备机制。
                            addIndexToLoaded(objIndex);
                            indexRef.current = objIndex;
                            onRequestChange(objIndex);
                            cancel();
                        }
                    }
                }
                api.start(i => {
                    //if (i < indexRef.current - 1 || i > indexRef.current + 1)   return { display: 'none' };
                    const x = (i - indexRef.current) * width + (active ? mx : 0);
                    const scale = 1;
                    return { x, scale, display: 'block' };
                });
            },
            {
                swipe: {velocity: 0.01},
                filterTaps: true,
                pointer: {mouse: !touchScr },
                // pointer: {touch: true, mouse: !touchScr, },      //嵌套Pager拉拽避免拉动父辈层和search input的cancel在电脑模式点击能清空的要求正好冲突了。
                triggerAllEvents: !carousel,       //嵌套在另外一个Pager底下的Pager设置true。和輪播模式衝突，輪播下菜單無法點按。
                enabled: !noBind,
            }
        );

        //visibility:hidden替换display:none, 组件状态被轮播Tabs切换以后均能保留;

        React.useEffect(() => {
            loaded.add(index);
            indexRef.current=index;
            api.start(i => {
                //if (i < indexRef.current - 1 || i > indexRef.current + 1)   return { display: 'none' };
                const x = (i - indexRef.current) * width;
                const scale =  1;
                return { x, scale, display: 'block' }
            })
        }, [index, width, api, loaded]);


        return (
            <div className="Pager"  key={id}
                 ref={containerRef}
                 style={{
                     position: 'relative',
                     overflow: 'hidden',
                     width: "100%",
                     height: "100%",
                     ...style,
                 }}
                 {...other}
            >
                {anmprops.map(({ x, display, scale }, i) =>{
                    const child= renderableChildren[i];
                    //传递给子页面： ref是一个动作函数(this) ？绑定时刻会执行ref函数: 非当前轮播儿子组件的直接"aria-hidden"隐藏?。
                    const sonprops = {
                        key: i,
                        tabIndex: index === i ? 0 : -1,
                        "aria-hidden": i !== index,
                    };
                    //代表已经允许mount加载了: load=true的儿子才能算正常DOM。load=false儿子说明未能够mount进来。允许很长队列只需要加载一部分儿子。
                    const load = !lazyLoad || index === i || loaded.has(i);

                    return(
                        <animated.div className='Pgbind'
                                      {...bind()}
                                      key={i}
                                      style={{ display, x }}
                                      css={{
                                          position: 'absolute',
                                          width: "100%",
                                          height: "100%",
                                          touchAction: 'none',
                                      }}
                        >
                            <animated.div className='Page_pane'
                                          style={{
                                              scale,
                                              ...paneStyle
                                          }}
                                          css={{
                                              touchAction: 'none',
                                              width: "100%",
                                              height: "100%",
                                          }}
                                          {...sonprops}
                            >
                                {load && child}
                            </animated.div>
                        </animated.div>
                    ); })}
            </div>
        );
    }
);



/*更为底层API使用例子 参考@use-gesture/react包文档       #手势库
    Tap点击，Pan小幅度拉动，Swipe快划过；Pinch捏住两个手指缩放，Rotate两个手指扭动。# Drag通用含义；
  function doDrag(state : any) {
        const { active, movement: [mx], direction: [xDir] , cancel, event, swipe:[swpx,swpy],tap,
                last,  velocity: [vx, vy], canceled, pinching } = state;
        console.log("拉动D",state,"active",active,"号序",indexRef.current,"canceled:",canceled,"快滑",swpx);
        event.stopPropagation();
        api.start(i => {
            if (i < indexRef.current - 1 || i > indexRef.current + 1) return { display: 'none' }
            const x = (i - indexRef.current) * width + (active ? mx : 0)
            const scale = active ? 1 - Math.abs(mx) / width / 2 : 1
            return { x, scale, display: 'block' }
        })
    }
    function doDragStart(state : any) {
        const { active, movement: [mx], direction: [xDir] , cancel, event, swipe:[swpx,swpy],tap,
            last,  velocity: [vx, vy], canceled, } = state;
        api.start(i => {
            if (i < indexRef.current - 1 || i > indexRef.current + 1) return { display: 'none' }
            const x = (i - indexRef.current) * width + (active ? mx : 0)
            const scale = active ? 1 - Math.abs(mx) / width / 2 : 1
            return { x, scale, display: 'block' }
        })
    }
    function doDragEnd(state : any) {
        const { active, movement: [mx], direction: [xDir] , cancel, event, swipe:[swpx,swpy],tap,
            last,  velocity: [vx, vy], canceled, } = state;
        event.stopPropagation();
        if (!active && !canceled && Math.abs(swpx) > 0) {
            let Old=indexRef.current;
            let childCount=pages.length;
            const idxNew= indexRef.current + (swpx > 0 ? -1 : 1);
            const objIndex= (idxNew > childCount - 1)? (childCount - 1) : idxNew<0? 0 :idxNew;
            indexRef.current = objIndex;
            console.log("拉动End Old",Old,"NEW=",indexRef.current);
            cancel()
        }
        api.start(i => {
            if (i < indexRef.current - 1 || i > indexRef.current + 1) return { display: 'none' }
            const x = (i - indexRef.current) * width + (active ? mx : 0)
            const scale = active ? 1 - Math.abs(mx) / width / 2 : 1
            return { x, scale, display: 'block' }
        })
    }
    const bind 1= useGesture(
        {
            onDrag: (state) => doDrag(state),
            onDragStart: (state) => doDragStart(state),
            onDragEnd: (state) => doDragEnd(state),
        },
       {  }
    )
   const bind 2= useDrag((state) => {
        const { active, axis, movement: [mx,], direction: [xDir,] , cancel, event, swipe:[swpx,],
                       tap,first,last,  canceled, type, intentional } = state;
         event.stopPropagation();   //不加双层轮播有问题
         const normaltrigs= ( !canceled && (Math.abs(swpx)>0 || Math.abs(mx)>=width*0.1) );
         const trigged= !active && axis==='x' && (normaltrigs );
         if(trigged){
            let Old=indexRef.current;
            const idxNew= indexRef.current + (xDir>0 ? -1 : 1);
            const objIndex= (idxNew > childCount - 1)? (childCount - 1) : idxNew<0? 0 :idxNew;
            if(normaltrigs){
                if(objIndex===Old)
                    onNestDragEnd && onNestDragEnd([xDir,0]);
                else {
                    addIndexToLoaded(objIndex);
                    indexRef.current = objIndex;
                    onRequestChange(objIndex);
                }
            } else {
                if(type==="pointercancel" && !canceled){
                }
            }
           cancel();
         }
         else{
             if(!canceled && axis!=='x') {
                 console.log("被 cancel()",type);
                 cancel();
             }
         }
        api.start(i => {
            if (i < indexRef.current - 1 || i > indexRef.current + 1) return { display: 'none' }
            const x = (i - indexRef.current) * width + (active ? mx : 0)
            const scale = active ? 1 - Math.abs(mx) / width / 2 : 1
            return { x, scale, display: 'block' }
        })
    } ,
    {
        enabled: !noBind,
        swipe: {velocity: 0.1},
        filterTaps: true,
    });
react-spring快捷定义标识:
    x, y, z
    translate, translateX, translateY, translate3d
    rotate, rotateX, rotateY, rotate3d
    scale, scaleX, scaleY, scale3d
    skew, skewX, skewY
    matrix, matrix3d
* */
